package com.monitor.util;

import com.monitor.model.*;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Component
public class ElasticSearchCombineDataUtil {

    /**
     * 默认规则url分组,层级最深4级：/>/km/>/km/review/>/km/review/doc/
     * @param httpLogs
     * @return
     */
    public ElasticSearchOverView combineData(List<AccessLog> httpLogs) {
        // 模块级别聚合
        Map<String, List<AccessLog>> urlLogMap = new HashMap<>();
        // 默认规则url分组,层级最深4级：/>/km/>/km/review/>/km/review/doc/
        httpLogs.forEach(httpLog -> groupByUrl(urlLogMap, httpLog));

        List<ElasticSearchModuleView> moduleViews = new ArrayList<>();
        for (Map.Entry<String, List<AccessLog>> urlLogEntry : urlLogMap.entrySet()) {
            String urlKey = urlLogEntry.getKey();
            List<AccessLog> moduleLogs = urlLogEntry.getValue();
            ElasticSearchModuleView moduleView = new ElasticSearchModuleView();
            moduleView.setPrefixUrl(urlKey);
            String parentUrl = getParentUrl(urlKey);
            moduleView.setParentUrl(parentUrl);
            //采集指标数据
            ElasticSearchBasicData basicData = getBasicData(moduleLogs);
            BeanUtils.copyProperties(basicData,moduleView);

            //抽象refer层聚合
            Map<String, List<AccessLog>> abstractReferMap = new HashMap<>();
            List<ElasticSearchAbstractReferView> abstractReferViews = new ArrayList<>();
            moduleLogs.forEach(moduleLog->{
                String referer = moduleLog.getReferer();
                String abstractRefer = replaceParam(referer);
                List<AccessLog> referLogs = abstractReferMap.get(abstractRefer);
                if (referLogs == null) {
                    referLogs = new ArrayList<>();
                }
                referLogs.add(moduleLog);
                abstractReferMap.put(abstractRefer, referLogs);
            });
            for (Map.Entry<String, List<AccessLog>> abstractReferEntry : abstractReferMap.entrySet()) {
                String abstractRefer = abstractReferEntry.getKey();
                List<AccessLog> abstractReferLogs = abstractReferEntry.getValue();
                ElasticSearchAbstractReferView abstractReferView = new ElasticSearchAbstractReferView();
                ElasticSearchBasicData abstractReferBasicData = getBasicData(abstractReferLogs);
                BeanUtils.copyProperties(abstractReferBasicData, abstractReferView);
                abstractReferViews.add(abstractReferView);
                //refer层聚合
                List<ElasticSearchReferView> referViews = new ArrayList<>();
                Map<String, List<AccessLog>> referMap = new HashMap<>();
                abstractReferLogs.forEach(abstractReferLog -> {
                    String referer = abstractReferLog.getReferer();
                    List<AccessLog> referLogs = referMap.get(referer);
                    if (referLogs == null) {
                        referLogs = new ArrayList<>();
                    }
                    referLogs.add(abstractReferLog);
                    referMap.put(referer, referLogs);
                });
                for (Map.Entry<String, List<AccessLog>> referEntry : referMap.entrySet()) {
                    String refer = referEntry.getKey();
                    List<AccessLog> referLogs = referEntry.getValue();
                    ElasticSearchReferView referView = new ElasticSearchReferView();
                    ElasticSearchBasicData referBasicData = getBasicData(referLogs);
                    BeanUtils.copyProperties(referBasicData, referView);
                    referViews.add(referView);
                    //url层聚合
                    Map<String, List<AccessLog>> urlMap = new HashMap<>();
                    List<ElasticSearchUrlView> urlViews = new ArrayList<>();
                    referLogs.forEach(referLog->{
                        String url = referLog.getUrl();
                        List<AccessLog> urlLogs = urlMap.get(url);
                        if (urlLogs == null) {
                            urlLogs = new ArrayList<>();
                        }
                        urlLogs.add(referLog);
                        urlMap.put(url, urlLogs);
                    });
                    for (Map.Entry<String, List<AccessLog>> urlEntry : urlMap.entrySet()) {
                        String url = urlEntry.getKey();
                        List<AccessLog> urlLogs = urlEntry.getValue();
                        ElasticSearchUrlView urlView = new ElasticSearchUrlView();
                        ElasticSearchBasicData urlBasicData = getBasicData(urlLogs);
                        BeanUtils.copyProperties(urlBasicData, urlView);
                        urlViews.add(urlView);
                    }
                    referView.setUrlViews(urlViews);
                }
                abstractReferView.setReferViews(referViews);
            }
            moduleView.setAbstractReferViews(abstractReferViews);
            moduleViews.add(moduleView);
        }

        // 模块数据转化成树结构
        ToTreeUtil<ElasticSearchModuleView, String> treeUtil = new ToTreeUtil<>(moduleViews, ElasticSearchModuleView::getPrefixUrl, ElasticSearchModuleView::getParentUrl, ElasticSearchModuleView::setChildModule);
        List<ElasticSearchModuleView> elasticSearchModuleViews = treeUtil.build();

        ElasticSearchOverView overView = new ElasticSearchOverView();
        overView.setModuleViews(elasticSearchModuleViews);
        ElasticSearchBasicData basicData = getBasicData(httpLogs);
        BeanUtils.copyProperties(basicData, overView);
        return overView;

    }

    private void groupByUrl(Map<String, List<AccessLog>> urlLogMap, AccessLog httpLog) {
        String uri = httpLog.getUrl();
        Matcher matcher = Pattern.compile("/").matcher(uri);
        int depth = 0;
        while (matcher.find()) {
            if (depth > 3) {
                break;
            }
            String urlKey = uri.substring(0, matcher.end());
            List<AccessLog> logs = urlLogMap.get(urlKey);
            if (logs == null) {
                logs = new ArrayList<>();
            }
            logs.add(httpLog);
            urlLogMap.put(urlKey, logs);
            depth++;
        }
    }

    private String getParentUrl(String uri) {
        List<String> keys = new ArrayList<>();
        Matcher matcher = Pattern.compile("/").matcher(uri);
        while (matcher.find()) {
            keys.add(uri.substring(0, matcher.end()));
        }
        if (keys.size() > 1) {
            return keys.get(keys.size() - 2);
        }
        return "";
    }

    private String replaceParam(String url) {
        Pattern pattern = Pattern.compile("[^=]*[&]");
        Matcher m = pattern.matcher(url);
        String x = m.replaceAll("@");
        int i1 = x.lastIndexOf("=");
        if (i1 != -1) {
            return x.substring(0, i1).concat("=@");
        }
        return url;
    }

    private ElasticSearchBasicData getBasicData(List<AccessLog> httpLogs) {
        ElasticSearchBasicData basicData = new ElasticSearchBasicData();
        int hits = httpLogs.size();
        basicData.setHits(hits);
        int visitors = httpLogs.stream().collect(Collectors.groupingBy(AccessLog::getIp)).size();
        basicData.setVisitors(visitors);

        Double respTime = httpLogs.stream().map(accessLog -> Double.valueOf(accessLog.getResp_time())).reduce(Double::sum).get();
        double averageResp = respTime / hits;
        basicData.setAverageResponseTime(String.valueOf(averageResp));
        List<ResponseCodeStatistic> responseCodeStatistics = new ArrayList<>();
        Map<String, List<AccessLog>> codeCollect = httpLogs.stream().collect(Collectors.groupingBy(AccessLog::getStatus));
        for (Map.Entry<String, List<AccessLog>> codeEntry : codeCollect.entrySet()) {
            ResponseCodeStatistic responseCodeStatistic = new ResponseCodeStatistic();
            responseCodeStatistic.setCode(codeEntry.getKey());
            int codeHits = codeEntry.getValue().size();
            responseCodeStatistic.setHits(codeHits);
            responseCodeStatistic.setPercentage(String.valueOf(100 * codeHits / hits));
            responseCodeStatistics.add(responseCodeStatistic);
        }
        basicData.setResponseCodeStatistic(responseCodeStatistics);
        return basicData;
    }

}
